<?php
namespace Websocket;

class WebsocketBuffer {
    protected $_currentPackage;
    protected $_waitingCompletedPackage;
    protected $_content;
    protected $_completed;
    protected $_fullPackageLength;
    protected $_fin;
    protected $_close;
    protected $_completedContents = [];
    
    /**
     * 装载数据
     * @param $package
     * @return $this
     */
    public function load($package) {
        $nextPackage = null;
        
        if($this->_waitingCompletedPackage) {
            $this->_currentPackage .=  $package;
            
            if(strlen($this->_currentPackage) >= $this->_fullPackageLength) {
                $package = $this->_currentPackage;
                $this->_waitingCompletedPackage = false;
            } else {
                return $this;
            }
        }
        
        if(!$package) {
            return $this;
        }
        
        $result = self::parsePackage($package);
    
        //连接关闭
        if($result['opcode'] == 8) {
            $this->_close = true;
            return $this;
        }
    
        if(!$result['completed']) {
            $this->_waitingCompletedPackage = true;
            $this->_fullPackageLength = $result['full_package_length'];
            $this->_currentPackage = $package;
            return $this;
        }
    
        $this->_content .= $result['payload'];
        
        if($result['fin']) {
            $this->_completedContents []= $this->_content;
            $this->_content = '';
        }
        
        if($result['next_package']) {
            $this->load($result['next_package']);
        }
        
        return $this;
    }
    
    public function hasMessage() {
        return !empty($this->_completedContents);
    }
    
    public function fetchMessage() {
        return array_shift($this->_completedContents);
    }
    
    public function isClosed() {
        return $this->_close;
    }
    
    public function fetchAllMessages() {
        $content = $this->_completedContents;
        $this->_completedContents = [];
        return $content;
    }
    
    static function parsePackage($content) {
        $char1 = ord($content[0]);
        $fin = ($char1 & 0x80) >> 7;
        $rsv1 = ($char1 & 0x40) >> 6;
        $rsv2 = ($char1 & 0x20) >> 5;
        $rsv3 = ($char1 & 0x10) >> 6;
    
        $char2 = ord($content[1]);
        $opcode = $char1 & 0xF;
    
        if($opcode == 8) {
            return [
                'fin' => $fin,
                'opcode' => $opcode,
            ];
        }
    
        $mask = ($char2 & 0x80) >> 7;
        $payloadLength = $char2 & 0x7F;
        $payloadLengthFlag = $payloadLength;
        $maskKeyPosition = 2;
    
        if($payloadLength == 126) {
            $char3 = ord($content[2]);
            $char4 = ord($content[3]);
        
            $payloadLength = ($char3 << 8) + $char4;
            $maskKeyPosition = 4;
        } else if($payloadLength == 127) {
            $hex =
                base_convert(ord($content[2]), 10, 16)
                .base_convert(ord($content[3]), 10, 16)
                .base_convert(ord($content[4]), 10, 16)
                .base_convert(ord($content[5]), 10, 16)
                .base_convert(ord($content[6]), 10, 16)
                .base_convert(ord($content[7]), 10, 16)
                .base_convert(ord($content[8]), 10, 16)
                .base_convert(ord($content[9]), 10, 16)
            ;
        
            $hex = ltrim($hex, '0');
            $payloadLength = intval(base_convert($hex, 16, 10));
            $maskKeyPosition = 10;
        }
    
        // $fullPayloadLength = $payloadLengthFlag >= 126 ? $payloadLength : $payloadLengthFlag;
        $maskKey = '';
        
        if($mask) {
            $maskKey = substr($content, $maskKeyPosition, 4);
            $head = substr($content, 0, $maskKeyPosition + 4);
            $payload = substr($content, $maskKeyPosition + 4, $payloadLength);
            $nextPackage = substr($content, $maskKeyPosition + 4 + $payloadLength);
        } else {
            $head = substr($content, 0, $maskKeyPosition);
            $payload = substr($content, $maskKeyPosition, $payloadLength);
            $nextPackage = substr($content, $maskKeyPosition + $payloadLength);
        }
    
        $maskKeyAry = [];
        $currentPayloadLength = strlen($payload);
        $completed = $currentPayloadLength == $payloadLength;
        
        //mask转换
        if($completed && $mask) {
            $maskKeyAry = array_map(function ($r) {return ord($r);}, str_split($maskKey, 1), $maskKeyAry);
        
            for($i=0; $i<$currentPayloadLength; $i++) {
                $payload[$i] = chr(ord($payload[$i]) ^ $maskKeyAry[$i%4]);
            }
        }
    
        $headLen = strlen($head);
        
        return [
            'completed' => $completed,
            'fin' => $fin,
            'head' => $head,
            'head_length' => $headLen,
            'package_length' => strlen($content),
            'full_package_length' => $headLen + $payloadLength,
            'payload' => $payload,
            'opcode' => $opcode,
            'mask' => $mask,
            'mask_key' => $maskKey,
            'mask_key_ary' => $maskKeyAry,
            'rsv1' => $rsv1,
            'rsv2' => $rsv2,
            'rsv3' => $rsv3,
            'payload_length_flag' => $payloadLengthFlag,
            'payload_length' => $currentPayloadLength,
            'full_payload_length' => $payloadLength,
            'next_package' => $nextPackage
        ];
    }
}
